﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.Routing;
using Bamboo.Helpers;

namespace VietThinks.Web.Mvc
{
    public static partial class HtmlExtensions
    {
        public static TableGridBuilder<T> TableGrid<T>(this HtmlHelper htmlHelper, int gridColumns, IList<T> items)
        {
            return new TableGridBuilder<T>(gridColumns, items);
        }
    }

    public class TableGridBuilder<T> : GridBuilder<T>
    {
        private Func<T, string> cellHtmlContentSelector;
        private IEnumerable<Func<T, string>> cellHiddenValueSelectors;

        public TableGridBuilder(int gridColumns, IList<T> items)
            : base(gridColumns, items)
        {
            this.cellHtmlContentSelector = i => i.ToStringEx();
            this.cellHiddenValueSelectors = new List<Func<T, string>>();
        }

        protected override void BeginGrid(StringBuilder stringBuilder)
        {
            stringBuilder.Append(HtmlElement.Table.BuildOpenTag(this.gridHtmlAttributes));
        }

        protected override void EndGrid(StringBuilder stringBuilder)
        {
            stringBuilder.Append(HtmlElement.Table.ClosingTag);
        }

        protected override void BeginRow(StringBuilder stringBuilder)
        {
            stringBuilder.Append(HtmlElement.Tr.BuildOpenTag(this.gridHtmlAttributes));
        }

        protected override void EndRow(StringBuilder stringBuilder)
        {
            stringBuilder.Append(HtmlElement.Tr.ClosingTag);
        }

        protected override void BeginCell(StringBuilder stringBuilder)
        {
            stringBuilder.Append(HtmlElement.Td.BuildOpenTag(this.cellHtmlAttributes));
        }

        protected override void EndCell(StringBuilder stringBuilder)
        {
            stringBuilder.Append(HtmlElement.Td.ClosingTag);
        }

        protected override void GenerateCellContent(StringBuilder stringBuilder, T item)
        {
            stringBuilder.Append(this.cellHtmlContentSelector(item));
            
            foreach (var selector in this.cellHiddenValueSelectors)
            {
                selector(item);
            }
        }

        protected override void GenerateTrailingCells(StringBuilder stringBuilder)
        {
            stringBuilder.Append(String.Format("<td colspan='{0}'></td>", this.gridColumns - (this.items.Count % this.gridColumns)));
        }

        private object gridHtmlAttributes;
        private object rowHtmlAttributes;
        private object cellHtmlAttributes;

        public TableGridBuilder<T> GridHtmlAttributes(object htmlAttributes)
        {
            this.gridHtmlAttributes = htmlAttributes;

            return this;
        }

        public TableGridBuilder<T> RowHtmlAttributes(object htmlAttributes)
        {
            this.rowHtmlAttributes = htmlAttributes;

            return this;
        }

        public TableGridBuilder<T> CellHtmlAttributes(object htmlAttributes)
        {
            this.cellHtmlAttributes = htmlAttributes;

            return this;
        }


        public TableGridBuilder<T> CellHtmlContent(Func<T, string> selector)
        {
            this.cellHtmlContentSelector = selector;

            return this;
        }

        public TableGridBuilder<T> CellHiddenValue(params Func<T, string>[] selectors)
        {
            this.cellHiddenValueSelectors = selectors;

            return this;
        }
    }

    public abstract class GridBuilder<T>
    {
        protected int gridColumns;
        protected IList<T> items;

        public GridBuilder(int gridColumns, IList<T> items)
        {
            Debug.Assert(items != null);

            this.gridColumns = gridColumns;
            this.items = items;
        }

        public string Build()
        {
            StringBuilder stringBuilder = new StringBuilder();

            bool rowOpened = false;

            this.BeginGrid(stringBuilder);

            for (int i = 0; i < this.items.Count; i++)
            {
                if (i % this.gridColumns == 0)
                {
                    rowOpened = true;

                    this.BeginRow(stringBuilder);
                }

                this.BeginCell(stringBuilder);
                this.GenerateCellContent(stringBuilder, this.items[i]);
                this.EndCell(stringBuilder);

                if (i % this.gridColumns == this.gridColumns - 1)
                {
                    this.EndRow(stringBuilder);

                    rowOpened = false;
                }
            }

            if (rowOpened)
            {
                this.GenerateTrailingCells(stringBuilder);

                this.EndRow(stringBuilder);
            }

            this.EndGrid(stringBuilder);

            return stringBuilder.ToString();
        }

        public override string ToString()
        {
            return this.Build();
        }

        protected abstract void BeginGrid(StringBuilder stringBuilder);
        protected abstract void EndGrid(StringBuilder stringBuilder);
        protected abstract void BeginRow(StringBuilder stringBuilder);
        protected abstract void EndRow(StringBuilder stringBuilder);
        protected abstract void BeginCell(StringBuilder stringBuilder);
        protected abstract void EndCell(StringBuilder stringBuilder);
        protected abstract void GenerateCellContent(StringBuilder stringBuilder, T item);
        protected abstract void GenerateTrailingCells(StringBuilder stringBuilder);
    }    
}
